"use strict";

Components.utils.import("resource://gre/modules/PlacesUtils.jsm")
//  resource://gre/modules/NewTabUtils.jsm
if("undefined" == typeof(proNewTab))
{
    var proNewTab = { version: "0.5.0" }    //  install.rdf
}

    //  const thequery = [ "div > vbox.pronewtab", "div > hbox.pronewtab" ]
proNewTab.midboxQuery = [ "div > vbox.pronewtab", "div > hbox.pronewtab" ]
proNewTab.dlgCleanerSetitle = "Clearing the list of the blocked links."
proNewTab.style2box = {
        hbox : [ "top: 0px; left: calc(50% - 38px);",   "bottom: 0px; left: calc(50% - 38px);"  ],
        vbox : [ "right: 2px; top: calc(50% - 38px);",  "left: 2px; top: calc(50% - 38px);" ],
        box  : [ "left: calc(50% + 32px); top: 0px;",   "top: calc(50% + 32px); right: 0px;"    ]
    }

proNewTab.menu2icon = [
            "chrome://pronewtab/skin/asterisk2.png",
            "chrome://pronewtab/skin/oneday2.png",
            "chrome://pronewtab/skin/saturn2.png" ]
proNewTab.small2icon = [
            "chrome://pronewtab/skin/small-asterisk.png",
            "chrome://pronewtab/skin/small-day.png",
            "chrome://pronewtab/skin/small-saturn.png" ]

    //  save original entrance
proNewTab.orgWideUpdate     = gPage.update.bind(gPage);
proNewTab.orgNarrowUpdate   = gUpdater.updateGrid.bind(gUpdater);

proNewTab.value2menuitem = function (anest, avalue)
{
    let thequery = "menuitem[value={'filter'}]";
        thequery = thequery.replace("={'filter'}]", [ "=", avalue, "]" ].join("'"));
    return anest.querySelector(thequery);
}

proNewTab.alertEmpty = function(anewval)
{
    let themsg = "There is currently no browsing history available for the selected time period."
    let thebox = document.documentElement.querySelector("box.radionewtab") || {};
        themsg = thebox.statusText || themsg;
        thebox = thebox.firstElementChild;
    if(thebox)
    {
        let thetitle = thebox.tooltipText;
        let themenu = this.value2menuitem(thebox, anewval);
            thetitle = [ thetitle, thebox.statusText, themenu.label ].join(" ").toLowerCase();
        Services.prompt.alert(window, thetitle, themsg)
    }
    else
        window.alert(themsg);
}

proNewTab.neoUpdate = function(aCallback)
{
    let afilter = this.adaptor || {}, theobj = window.gLinks;
    if (afilter.status)
    try {
        window.gLinks = afilter;
    //  gGrid.refresh(); from newtab.js, can be call(gPage)
        this.orgNarrowUpdate(aCallback);
        window.setTimeout( function() { proNewTab.afterPopulateThumnails() }, 10);
    }
    finally { window.gLinks = theobj }
    else this.orgNarrowUpdate(aCallback);
}

proNewTab.afterPopulateThumnails = function()
{
    let query = "a.newtab-link[href^='javascript:proNewTab']";
    let thenode = document.documentElement.querySelector(query) || {};
        thenode.title = proNewTab.dlgCleanerSetitle;
}

proNewTab.exUpdate = function()
{
    let afilter = this.adaptor || {}, theobj = window.gLinks;
    if (afilter.status)
    try {
        window.gLinks = afilter;
    //  gGrid.refresh(); from newtab.js, can be call(gPage)
        this.orgWideUpdate();
        window.setTimeout( function() { proNewTab.afterPopulateThumnails() }, 10);
    }
    finally { window.gLinks = theobj }
    else this.orgWideUpdate();

    try {
        this.updateBtns("hbox", "gridRows");
        this.updateBtns("vbox", "gridColumns");
    }
    catch (err) {
        Components.utils.reportError(err)
    }
}

 //  return proNewTab.orgModLinks.call(gLinks);
 //  .newLinks for gLinks of newTab.xul
proNewTab.adaptor = {   //oNewTab.filter = {
    status   : 0,     //  asterisk, day, week, month
    _links   : [],  //  set of links
    orgLinks : window.gLinks,

//  **************************************
//  *   adoption part   *

  //   * Resets the links cache.
  resetCache : function () { this._links = null },

//  * Populates the cache with fresh links from the current provider.  
  populateCache: function (aCallback, aForce)
    { this.orgLinks.populateCache(aCallback, aForce) },

  _getMergedProviderLinks: function ()
    {
// window.console.log("_dvk_dbg_, _getMergedProviderLinks: ", this._links);
        return this._links;
    },
    //  to be as gLinks.getLinks, main charge
  getLinks: function()
  {
    let sequence = this.orgLinks.getLinks.call(this);
    if (sequence.length > gPinnedLinks.links.length)
        return sequence;    //  usual case

    //  dump("_dvk_dbg_, pin len:\t"); dump(gPinnedLinks.links.length); dump("\n");
    //  dump("_dvk_dbg_, trunk len:\t"); dump(sequence.length); dump("\n");
    let theone = sequence.filter(function (alink) { return (alink) });
    let thetow = gPinnedLinks.links.filter(function (alink) { return (alink) });

    let theobj = { url: "javascript:proNewTab.clearBlockedLinks()", 
                  title: proNewTab.dlgCleanerSetitle }; // type and etc ?
    if (theone.length == thetow.length)
        if (theone.length < sequence.length)
        {
            for(let i = 0; i < sequence.length; ++i)
                if(!(sequence[i]))
                {
                    sequence[i] = theobj;
                    break;
                }
        }
            else sequence.push(theobj);

    return sequence;    //  and works up by pin and block link
  },
//  *   adoption part   *
//  **************************************

 // display xul:menupopup onpopupshowing=""
    show : function(amenu)
    {
        let thelist = amenu.querySelectorAll("menuitem[type]");
        for(let i = 0; i < thelist.length; ++i)
        {
            let theval = parseInt(thelist[i].value || (0));
            if (theval == this.status)
                thelist[i].setAttribute("checked", true)
            else
                thelist[i].removeAttribute("checked");
        }
    },

 // code flow : setLinks and similar
    update : function()
    {
    //  dump("_dvk_dbg_, update box.radionewtab\n");
        let thelist = document.documentElement.querySelectorAll("box.radionewtab");
        for(let i = 0; i < thelist.length; ++i)
        {    //          let thelabel= "OFF";
            let thebtn  = thelist[i].firstElementChild;
            let theval = (this.status >> 1) ? (2) : this.status;
                thebtn.setAttribute("image", proNewTab.small2icon[theval]);
        }
    },

    toggle : function(abtn)
    {
        let result = 0;
        let thelist = abtn.querySelectorAll("menuitem[type]");
        for (let i = 0; i < thelist.length; ++i)
            if(thelist[i].getAttribute("checked"))
            {
                result = parseInt(thelist[i].value || "0");
                break;
            }
        this.occured(result);
    },

    setLinks : function(anewval, sequence)
    {        
        if(sequence) this._links = sequence
            else this._links = [];
        this.status = anewval; //  then update self and page
    //      if(anewval) window.setTimeout( function(anode)
        window.setTimeout( function(apage) { apage.update() }, 0, gPage );
        window.setTimeout( function(anobj) { anobj.update() }, 0, this );        
    },

    occured : function(anewval) //  like populateCache
    {
        const ERR_PREFIX = "Error is occured with next message"
            let thelen = Object.keys(gBlockedLinks.links).length;
        const HISTORY_RESULTS_LIMIT = gGridPrefs.gridColumns * gGridPrefs.gridRows + thelen;

        if((this._links) && (this.status == anewval)) return;

    let callback = {
        anchor : window,//  host
        sequence : [],  //  result
  //    getLinks: function PlacesProvider_getLinks(aCallback) {
        handleResult: function (aResultSet)
        {
            let row;
            try {
                while (row = aResultSet.getNextRow())
                {
                    let thurl = row.getResultByIndex(1);
                    let title = row.getResultByIndex(2);
      // Check that we're not accepting URLs which would inherit the caller's principal (such as javascript: or data:).
                    if (this.anchor.gLinkChecker.checkLoadURI(thurl))
                    {
                        let lastVisitDate = row.getResultByIndex(5);
                        let frecency = row.getResultByIndex(12);
                        this.sequence.push({ 
                            url: thurl, title: title, type: "history",
                              frecency: frecency,
                              lastVisitDate: lastVisitDate,
                              bgColor: "transparent",
                              imageURI: null,
                        });
                    }
                }
            }
            catch (err) {
                Components.utils.reportError(err)
            }
        },

        handleError: function (aError) 
        {   // Should we somehow handle this error?
            let amsg = [ ERR_PREFIX, aError ].join(": ");
            this.anchor.setTimeout( function() { proNewTab.adaptor.occured(0) }, 0 );
            this.anchor.alert(amsg);            
        },

        handleCompletion: function (aReason)
        {
            if(this.sequence && (this.sequence.length))
                this.anchor.proNewTab.adaptor.setLinks(anewval, this.sequence)
            else
                this.anchor.setTimeout( function(anewval) { window.proNewTab.alertEmpty(anewval) }, 0, anewval );
        }
    }   //  let callback = {

        //  options.sortingMode = options.SORT_BY_DATE_DESCENDING;
        // Query for MRU, descending. ? SORT_BY_LASTMODIFIED_DESCENDING
        // Execute the query.
        let theday = 24 * 60 * 60 * 1000000; // 24 hours in microseconds
        let query2 = PlacesUtils.history.getNewQuery();

        switch (anewval)
        {
            case (1) :  //  one day
                query2.endTimeReference = query2.TIME_RELATIVE_NOW;
                query2.endTime = 0; // now
                query2.beginTimeReference = query2.TIME_RELATIVE_NOW;
                query2.beginTime = - (theday); // 24 hours ago in microseconds
            break;
    
            case (7) :  //  past week
                query2.endTimeReference = query2.TIME_RELATIVE_TODAY;
                    query2.endTime = theday; // 24 hours toward in microseconds
                query2.beginTimeReference = query2.TIME_RELATIVE_TODAY;
                query2.beginTime = - (6 * theday); // 6 days * 24 hours ago in microseconds
            break;

            default: anewval = 0;
        }

        if(anewval)
        try {
            let options = PlacesUtils.history.getNewQueryOptions();
                options.maxResults  = HISTORY_RESULTS_LIMIT;
                options.sortingMode = options.SORT_BY_FRECENCY_DESCENDING;
            
            let thedb = PlacesUtils.history.QueryInterface(Components.interfaces.nsPIPlacesDatabase);
                thedb.asyncExecuteLegacyQueries([query2], 1, options, callback);
        }
        catch (err) {
            Components.utils.reportError(err)
        }
        else this.setLinks(0, null);
        return;
    },  //      occured : function(anewval) //  like populateCache
    //  proNewTab.adaptor
}

    //  uodates view of input btns
proNewTab.updateBtns = function(aprefix, asuffix)
{
    let selectors   = [ aprefix, "input.pronewtab-plus" ].join(" > ");
    let thelist     = document.documentElement.querySelectorAll(selectors);
    let i, thevalue = (gGridPrefs[asuffix] > 8); //   about 9
    for(i = 0; i < thelist.length; ++i)
    {
        thelist[i].disabled = (thevalue);
        thelist[i].title = (thevalue) ? "" : (thelist[i].statusText);
    }

    selectors   = [ aprefix, "input.pronewtab-minus" ].join(" > ");
    thelist     = document.documentElement.querySelectorAll(selectors);
    thevalue    = (gGridPrefs[asuffix] < 3);
    for(i = 0; i < thelist.length; ++i)
    {
        thelist[i].disabled = (thevalue);
        thelist[i].title = (thevalue) ? "" : (thelist[i].statusText);
    }
}

proNewTab.doCmd = function(thetag, anewvalue)
{
    const box2pref = { hbox : "browser.newtabpage.rows", 
                        vbox : "browser.newtabpage.columns" }

    if(anewvalue < 2) anewvalue = 2;
        else if (anewvalue > 9) anewvalue = 9;

    let retval = false; //  set new value
    let theval = Services.prefs.getIntPref(box2pref[thetag]);
    if (theval != anewvalue) try {
        Services.prefs.setIntPref(box2pref[thetag], anewvalue);
        retval = true;
        if (theval > anewvalue)
            window.setTimeout( function() 
                { proNewTab.squeezePinnedLink() } , 0);
    }
    catch(err)  {
        Components.utils.reportError(err)
    }
    return retval;
}

proNewTab.squeezePinnedLink = function() // NOTE: untested proc.
{
    let newvalue = (gGridPrefs.gridRows * gGridPrefs.gridColumns) - 1;
    let thelinks = gPinnedLinks.links;
    let index = (thelinks.length - 1);
    if(index > newvalue)    //  find free place, light version.
    if(!(thelinks[newvalue]))
    {
        let thepin = null;
        for( ; index > newvalue; --index) {
            thepin = thelinks.pop();
            if(thepin) break;
        }

        if(thepin) {
            gPinnedLinks.pin(thepin, newvalue);
            window.location.reload();
        }
            else gPinnedLinks.save();
    }
}

proNewTab.startup = function()
{
    //  target:     loads style2box to (h/v)box
    //  result:     place at the middle of border
    function placeDoubleBtns(aprefix, asuffix) {
        if(!asuffix) asuffix = "pronewtab";
        let thevalue, selectors = [ aprefix, asuffix ].join(".");
        let thelist = document.documentElement.querySelectorAll(selectors);
        for(let i = 0; i < thelist.length; ++i) {
            thevalue = proNewTab.style2box[aprefix][(i % 2)]
            thelist[i].setAttribute("style", thevalue);
        }
    }

    let i, thenode = document.getElementById("newtab-grid");
//  activate width and height of #newtab-grid.pronewtab > .newtab-cell
    window.setTimeout( function(anode) {
        anode.classList.add("pronewtab") }, 2, thenode );

//  twice every double btn    const thequery = [ "div > vbox.pronewtab", "div > hbox.pronewtab" ]
    let j = 0, selector = this.midboxQuery.join(" , "),
        side = [ "top", "bottom", "right", "left" ],
        thelist = document.documentElement.querySelectorAll(selector);
    for(i = 0; i < thelist.length; ++i) {
        thenode = thelist[i];
        let newnode = thenode.cloneNode(true);
            thenode.classList.add( side[j++] );
            newnode.classList.add( side[j++] );
            thenode.parentNode.appendChild(newnode);
    }

    placeDoubleBtns("hbox");
    placeDoubleBtns("vbox");
    placeDoubleBtns("box", "radionewtab");

    //  sets events to input btns by starts
    thelist = document.documentElement.querySelectorAll(".pronewtab > input[disabled]");
    for(i = 0; i < thelist.length; ++i)
    {
        thelist[i].addEventListener("click", this, false);
        thelist[i].addEventListener("keydown", this, false);
        thelist[i].addEventListener("keyup", this, false);
        thelist[i].statusText = thelist[i].title;
//        thelist[i].addEventListener("mouseover", this, false);
    }

    this.updateBtns("hbox", "gridRows");
    this.updateBtns("vbox", "gridColumns");

    thenode = document.documentElement.querySelector("div#newtab-scrollbox > input[type='text']");
    if (thenode) {
        proNewTab.dlgCleanerSetitle = thenode.value;
        thenode.parentNode.removeChild(thenode);
    }    
}

//  NOTE: remove as deprecate.
proNewTab.afterMouse = function(anode) {
        let thenode = anode.parentNode;
        if(!thenode) return;
        let thattr = "disabled";
        if(anode.disabled)
        {
            thenode.setAttribute("disabled", "true");
            thattr = "focused";
        }
        thenode.removeAttribute(thattr);
        return;
    }

proNewTab.handleEvent = function(anevt)
{
    const XUL_NAMESPACE = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    const class2side = {
            "pronewtab-minus"   : function(avalue) { return --avalue },
            "pronewtab-plus"    : function(avalue) { return ++avalue }  }
    const key2click =[ KeyEvent.DOM_VK_RETURN, KeyEvent.DOM_VK_SPACE ]
    const box2grid = { hbox : "gridRows", vbox : "gridColumns" }
    let i, theval, thenode, thelist;
    switch (anevt.type)
    {
        case "load" : {
            window.removeEventListener(anevt.type, this, false);
            this.startup();
            proNewTab.exUpdate = proNewTab.exUpdate.bind(proNewTab);
                gPage.update = proNewTab.exUpdate;

            thelist = document.documentElement.querySelectorAll("box.radionewtab");
            if(thelist.length)
            if(Services.prefs.getBoolPref("extensions.pronewtab.filter2display"))
            {
                proNewTab.neoUpdate = proNewTab.neoUpdate.bind(proNewTab);
                gUpdater.updateGrid = proNewTab.neoUpdate;

                for(i = 0; i < thelist.length; ++i)
                    thelist[i].removeAttribute("hidden");

        //      insert icon to right site of menu item
                    thelist = document.documentElement.querySelectorAll("menupopup > menuitem[type='radio']");
                for(i = 0; i < thelist.length; ++i)
                {
                    thenode = document.getAnonymousElementByAttribute(thelist[i], "anonid", "accel");
                    let newnode = document.createElementNS(XUL_NAMESPACE, "xul:image");
                    if(thenode && newnode)
                    {   //  newnode = document.createElement("image");
                        theval = parseInt(thelist[i].value || (0));
                        if(theval >> 1) theval = 2; //  avoid seven index
                        newnode.setAttribute("src", proNewTab.menu2icon[theval]);
                        newnode.className = "menu-iconic-icon";
                        thenode.appendChild(newnode);
                    }
                }

                thelist = document.documentElement.querySelectorAll("button[type='menu'] > menupopup");
                for(i = 0; i < thelist.length; ++i)
                    thelist[i].addEventListener("popupshowing", this, false);
    //  dump("_dvk_dbg_, button > menupopup:\t"); dump(thelist.length); dump("\n");
            }
            else
                for(i = 0; i < thelist.length; ++i)
                    thelist[i].parentNode.removeChild(thelist[i]);
        }
        break;  //  case "load"

        case "popupshowing" :   //  CHANGES: since v.33
            thelist = document.documentElement.querySelectorAll("div.newtab-site input.newtab-control[pinned]");
            if (!(thelist.length)) thelist = document.documentElement.querySelectorAll("div.newtab-site[pinned]");
            thenode = anevt.target.querySelector("menuitem.statustext[disabled]");
            if(thenode)
                if (thelist.length) thenode.setAttribute("description", thelist.length);
                    else thenode.setAttribute("description", thenode.statusText);
        break;

        case "click" : {
            let thetag = anevt.target.parentNode.localName;
            if (thetag in box2grid)
            if (anevt.target.className in class2side)
            {
                theval = gGridPrefs[ box2grid[thetag] ];
                theval = class2side[ anevt.target.className ](theval);
                theval = this.doCmd(thetag, theval);

                if (theval) // update active button
                window.setTimeout( function(anode) {
                        let thelist = anode.parentNode.querySelectorAll("input[type='button']");
                        if (anode.disabled) //  pause then ops
                            for(let i = 0; i < thelist.length; ++i)
                                if (!(thelist[i].disabled)) {
                                    thelist[i].focus();
                                    break;
                                }
                    }, 0, anevt.target);
            }
        }
        break;  //  case "click"

        case "mouseover" :  // unused event
            window.setTimeout( function(anode) { proNewTab.afterMouse(anode) }, 1, anevt.target);
        break;

        case "keydown" :
            if(key2click.indexOf(anevt.keyCode) + 1)
                if(!(anevt.target.disabled))
                {
                    anevt.target.addEventListener("blur", this, false);
                    anevt.target.parentNode.setAttribute("focused", "true");
                }
        break;

        case "keyup" :
            if(key2click.indexOf(anevt.keyCode) == (-1)) break;
        case "blur" :
            if(anevt.target.parentNode) {
                anevt.currentTarget.removeEventListener("blur", this, false);
                anevt.target.parentNode.removeAttribute("focused");
            }
        break;

        default :
            [ "pagehide", "unload" ].forEach( function(atype)
                { window.removeEventListener(atype, this, false); });
        break;
    }
}

proNewTab.maltoolTip2input = function()
{
    const malquery  = ".pronewtab > input[disabled]";
    let i, thequery = proNewTab.midboxQuery.join(" , ");
    let thelist = document.documentElement.querySelectorAll(thequery);
    if(thelist.length >> 2) //  if already diamond
    {
        for (i = 0; i < thelist.length; ++i)
            thelist[i].removeAttribute("tooltiptext");
    }
    else
    {
        thelist = document.documentElement.querySelectorAll(malquery);
        for (i = 0; i < thelist.length; ++i)
            thelist[i].title = thelist[i].parentNode.tooltipText;
    }
}

proNewTab.clearBlockedLinks = function()
{
    let thewin = window.openDialog(  "chrome://pronewtab/content/dlg.xul",
        "chrome://pronewtab/content/dlg.xul",
        "chrome,titlebar,centerscreen,modal,resizable" );
    if (thewin.returnValue) gPage.update();
}

 window.setTimeout( function() { proNewTab.maltoolTip2input() }, 777 );
    //  window.addEventListener("pagehide", proNewTab, false);
    //  window.addEventListener("unload", proNewTab, false);
    window.addEventListener("load", proNewTab, false);

if ((gGrid || {})._computeHeight) {
//  "chrome://browser/content/newtab/newTab.js"
//    window.removeEventListener("resize", gGrid);
//    window.console.log("gGrid._computeHeight: ", gGrid);
    gGrid._computeHeight = function(aRows) {
            let margin = document.querySelector("#newtab-margin-top").offsetHeight;
            return document.documentElement.clientHeight - (2 * margin) - 
                    document.querySelector("#newtab-search-container").offsetHeight;
        }
    document.getElementById("newtab-grid").removeAttribute("style");
//    return aRows * this._cellHeight + GRID_BOTTOM_EXTRA;
}
